package org.garden.id.generator.segment.service;

import org.garden.id.generator.segment.entity.Result;
import org.garden.id.generator.segment.entity.ResultCode;
import org.garden.id.generator.segment.entity.Segment;

import javax.sql.DataSource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class IdGenerator {

    private String type;

    private IdConfigService idConfigService;

    protected volatile Segment current;

    protected volatile Segment next;

    private volatile boolean isLoadingNext;

    private final Object lock = new Object();

    private final ExecutorService executorService = Executors.newSingleThreadExecutor();


    public IdGenerator() {
    }

    public IdGenerator(String type, DataSource dataSource, Integer retry, Integer loadPercent) {
        this.type = type;
        this.idConfigService = new IdConfigService(dataSource, retry, loadPercent);
    }

    public Long nextId() {
        while (true) {
            if (current == null) {
                loadCurrent();
                continue;
            }
            Result result = current.nextId();
            if (result.getCode() == ResultCode.OVER) {
                loadCurrent();
            } else {
                if (result.getCode() == ResultCode.LOADING) {
                    loadNext();
                }
                return result.getId();
            }
        }
    }

    private synchronized void loadCurrent() {
        if (current == null || !current.useful()) {
            if (next == null) {
                this.current = idConfigService.getNextSegment(type);
            } else {
                current = next;
                next = null;
            }
        }
    }

    private void loadNext() {
        if (next == null && !isLoadingNext) {
            synchronized (lock) {
                if (next == null && !isLoadingNext) {
                    isLoadingNext = true;
                    executorService.submit(new Runnable() {
                        @Override
                        public void run() {
                            next = idConfigService.getNextSegment(type);
                            isLoadingNext = false;
                        }
                    });
                }
            }
        }
    }
}
